/*Z OK 解析boot命令行参数，目前支持："console_port=xxx" "debug_port=xxx" "disable_iommu"
头两项默认3f8，后一项默认使能iommu */

/*
 * Copyright 2014, General Dynamics C4 Systems
 *
 * SPDX-License-Identifier: GPL-2.0-only
 */

#include <config.h>
#include <util.h>
#include <machine/io.h>
#include <arch/kernel/cmdline.h>
#include <arch/kernel/boot_sys.h>
#include <linker.h>
#include <plat/machine/io.h>

/* 'cmdline_val' is declared globally because of a C-subset restriction.
 * It is only used in cmdline_parse(), which therefore is non-reentrant.
 *//*Z 解析引导时命令行参数的临时字符串缓冲区 */
#define MAX_CMDLINE_VAL_LEN 1000
BOOT_BSS
char cmdline_val[MAX_CMDLINE_VAL_LEN];
/*Z 不好：头两个串没人用 */
/* workaround because string literals are not supported by C parser */
const char cmdline_str_max_num_nodes[]  = {'m', 'a', 'x', '_', 'n', 'u', 'm', '_', 'n', 'o', 'd', 'e', 's', 0};
const char cmdline_str_num_sh_frames[]  = {'n', 'u', 'm', '_', 's', 'h', '_', 'f', 'r', 'a', 'm', 'e', 's', 0};
const char cmdline_str_disable_iommu[]  = {'d', 'i', 's', 'a', 'b', 'l', 'e', '_', 'i', 'o', 'm', 'm', 'u', 0};
/*Z 小于等于空格（0x20）的字符均视为空白 */
static int is_space(char c)
{
    return c <= ' ';
}
/*Z 从cmdline字符串中，将opt串匹配的=后部分，拷贝到value中(不超过bufsize-1字节) ，
有重复的取最后一次出现。返回值-1无匹配，0有匹配但无值，其它为值字符串长度 */
static int UNUSED parse_opt(const char *cmdline, const char *opt, char *value, int bufsize)
{
    int len = -1;
    const char *optptr = NULL;

    while (true) {   /*Z 跳过空白 */
        for (; is_space(*cmdline) && (*cmdline != 0); cmdline++);
        if (*cmdline == 0) {    /*Z 一直检查到源串尾 */
            break;
        }
        /*Z 搜索串。不好：*optptr和*cmdline均无意义，因为非空白和相等包含它俩 */
        for (optptr = opt; *optptr && *cmdline && (*cmdline != '=') && !is_space(*cmdline)
             && (*optptr == *cmdline); optptr++, cmdline++);

        if (*optptr == '\0' && *cmdline == '=') {   /*Z 搜索成功 */
            cmdline++;

            for (len = 0; !is_space(*cmdline) && (len < bufsize - 1); cmdline++, len++) {
                value[len] = *cmdline;
            }
            if (bufsize) {  /*Z 不好：这里是为了防止bufsize=0，那bufsize<0呢 */
                value[len] = '\0';
            }
        }
        for (; !is_space(*cmdline); cmdline++); /*Z 跳过超长的或不匹配的串 */
    }

    return len;
}
/*Z 从cmdline字符串中，搜索opt串，空白分隔。返回值0无匹配，1有匹配 */
static int parse_bool(const char *cmdline, const char *opt)
{
    const char *optptr = NULL;

    while (1) {
        for (; is_space(*cmdline) && (*cmdline != 0); cmdline++);   /*Z 跳过空白 */
        if (*cmdline == 0) {
            return 0;
        }

        for (optptr = opt; *optptr && *cmdline && !is_space(*cmdline) && (*optptr == *cmdline); optptr++, cmdline++);

        if (*optptr == '\0' && is_space(*cmdline)) {
            return 1;
        } else {
            for (; !is_space(*cmdline); cmdline++); /*Z 跳过非空白 */
        }
    }
}
/*Z 将str字符串(逗号分隔的数值)转换成array_size大小的uint16_t array。不好：超范围会默默截断 */
static void UNUSED parse_uint16_array(char *str, uint16_t *array, int array_size)
{
    char *last;
    int   i = 0;
    int   v;

    while (str && i < array_size) {
        for (last = str; *str && *str != ','; str++);   /*Z 找到值结尾 */
        if (*str == 0) {
            str = 0;        /*Z 源串结束，下次循环退出 */
        } else {
            *str = 0;
            str++;
        }
        v = str_to_long(last);  /*Z 不好：long -> int -> uint16_t */
        if (v == -1) {
            array[i] = 0;
        } else {
            array[i] = v;
        }
        i++;
    }
}
/*Z 获取引导时的空白分隔的key=value（值间逗号分隔）命令行参数 */
void cmdline_parse(const char *cmdline, cmdline_opt_t *cmdline_opt)
{
#if defined(CONFIG_PRINTING) || defined(CONFIG_DEBUG_BUILD)
    /* use BIOS data area to read serial configuration. The BDA is not
     * fully standardized and parts are absolete. See http://wiki.osdev.org/Memory_Map_(x86)#BIOS_Data_Area_.28BDA.29
     * for an explanation */
    const unsigned short *bda_port = (unsigned short *)0x400;   /*Z COM1端口 */
    const unsigned short *bda_equi = (unsigned short *)0x410;   /*Z 已知硬件 */
    int const bda_ports_count       = (*bda_equi >> 9) & 0x7;   /*Z 串口数量 */
#endif

#ifdef CONFIG_PRINTING
    /* initialise to default or use BDA if available */
    cmdline_opt->console_port = bda_ports_count && *bda_port ? *bda_port : 0x3f8;
    /*Z 解析控制台端口号 */
    if (parse_opt(cmdline, "console_port", cmdline_val, MAX_CMDLINE_VAL_LEN) != -1) {
        parse_uint16_array(cmdline_val, &cmdline_opt->console_port, 1);
    }
    /*Z 初始化控制台端口 */
    /* initialise console ports to enable debug output */
    if (cmdline_opt->console_port) {
        serial_init(cmdline_opt->console_port);
        x86KSconsolePort = cmdline_opt->console_port;
    }
    /*Z BUG：printf用的是x86KSdebugPort，现在还无值，因此下面的打印无输出??????? */
    /* only start printing here after having parsed/set/initialised the console_port */
    printf("\nBoot config: parsing cmdline '%s'\n", cmdline);

    if (cmdline_opt->console_port) {
        printf("Boot config: console_port = 0x%x\n", cmdline_opt->console_port);
    }
#endif

#ifdef CONFIG_DEBUG_BUILD
    /* initialise to default or use BDA if available */
    cmdline_opt->debug_port = bda_ports_count && *bda_port ? *bda_port : 0x3f8;
    if (parse_opt(cmdline, "debug_port", cmdline_val, MAX_CMDLINE_VAL_LEN) != -1) {
        parse_uint16_array(cmdline_val, &cmdline_opt->debug_port, 1);
    }

    /* initialise debug ports */
    if (cmdline_opt->debug_port) {
        serial_init(cmdline_opt->debug_port);
        x86KSdebugPort = cmdline_opt->debug_port;
        printf("Boot config: debug_port = 0x%x\n", cmdline_opt->debug_port);
    }
#endif
    /*Z 默认使能iommu */
    cmdline_opt->disable_iommu = parse_bool(cmdline, cmdline_str_disable_iommu);
    printf("Boot config: disable_iommu = %s\n", cmdline_opt->disable_iommu ? "true" : "false");
}
